/*****************************************************************************
 * $Workfile:: eeprom.c                                                      $
 * $Revision:: 3                                                             $
 * $JustDate::  5/20/10                                                      $
 *
 *      GENIE V ECU
 *      serial eeprom
 *****************************************************************************/

#include "common.h"
#include "main.h"
#include "eeprom.h"
#include "serial.h"

#ifdef MANUAL_BIT_TOGGLING

#define EEPROM_PAGE_SIZE    64

struct Header
{
   UINT8 bMagic;           // data type
   UINT8 bLength;
   UINT8 bSum;             // data sum
   UINT8 bXor;
};

#define RECORD_MAX_LEN  (EEPROM_PAGE_SIZE * 2)
#define RECORD_HEADER_LEN sizeof( struct Header)
#define RECORD_DATA_LEN (RECORD_MAX_LEN - RECORD_HEADER_LEN )

struct
{
   struct Header NVM_Header;
   UINT8 abData[ RECORD_DATA_LEN ];
} NVM_Cache;


static UINT16 MagicToAddress(UINT8 bMagic);


/*******************************************************************************
 *
 * PROCEDURE:  MagicToAddress()
 *
 * DESCRIPTION: It converts Magic number to eeprom address.
 *              The record address must be alligned to page boundaries.
 *
 * VARIABLE NAME  R/W DESCRIPTION
 * ---------------- --- --------------------------------------------------------
 ******************************************************************************/

static UINT16
MagicToAddress(UINT8 bMagic)
{
   switch(bMagic)
   {
      default:
         return (UINT16) - 1;

      case RECORD_MAGIC_FAULT:
         return 0;

      case RECORD_MAGIC_LIFT_DRIVE:
         return EEPROM_PAGE_SIZE * 1;

      case RECORD_MAGIC_TIME:
         return EEPROM_PAGE_SIZE + (EEPROM_PAGE_SIZE / 8);

      //        case RECORD_MAGIC_TEST:
      //            return (EEPROM_PAGE_SIZE + (EEPROM_PAGE_SIZE / 4));

      case RECORD_MAGIC_BATTERY:
         return (EEPROM_PAGE_SIZE + (EEPROM_PAGE_SIZE / 2));

      //        case RECORD_MAGIC_TRIP_ANGLE:
      //            return (EEPROM_PAGE_SIZE + ((EEPROM_PAGE_SIZE * 5) / 8));
      //
      case RECORD_MAGIC_OPTION_1:
         return (EEPROM_PAGE_SIZE + ((EEPROM_PAGE_SIZE * 3) / 4));

      case RECORD_MAGIC_OPTION_2:
         return (EEPROM_PAGE_SIZE + (EEPROM_PAGE_SIZE * 3) / 4) + 8;
      case RECORD_MAGIC_OPTION_3:
         return (EEPROM_PAGE_SIZE + (EEPROM_PAGE_SIZE / 4)) + 8; //v 2

      case RECORD_MAGIC_ELEC3:
         return (EEPROM_PAGE_SIZE * 2l);

      case RECORD_MAGIC_SMALL_SLAB:
         return (EEPROM_PAGE_SIZE * 4l);

      case RECORD_MAGIC_ELEC1:
         return (EEPROM_PAGE_SIZE * 6l);

      case RECORD_MAGIC_ELEC2:
         return (EEPROM_PAGE_SIZE * 8l);

      case RECORD_LOAD_SENSING_ANGLE_SENSOR_ARRAY: // 128 bytes (64 words) for array storage
         return (EEPROM_PAGE_SIZE * 10l);

      case RECORD_LOAD_SENSING_PRESSURE_SENSOR_ARRAY: // 4 bytes (1 double word) for array storage
         return (EEPROM_PAGE_SIZE * 12l);

      case RECORD_NOLOAD_ANGLE_SENSOR_ARRAY:       // 128 bytes (64 words) for array storage
         return (EEPROM_PAGE_SIZE * 14l);

      case RECORD_NOLOAD_PRESSURE_SENSOR_ARRAY:    // 128 bytes (64 words) for array storage
         return (EEPROM_PAGE_SIZE * 16l);

      case RECORD_MAGIC_TRIP_ANGLE:    // 16 bytes (8 words) for storage
         return (EEPROM_PAGE_SIZE * 17l + ((EEPROM_PAGE_SIZE * 3) / 4));

      case RECORD_LOAD_SENSING_CALIBRATION_STATUS: // 16 bytes (8 words) for storage
         return (EEPROM_PAGE_SIZE * 26l);

      case RECORD_UP_LOAD_SENSING_ANGLE_SENSOR_ARRAY: // 128 bytes (64 words) for array storage
         return (EEPROM_PAGE_SIZE * 18l);

      case RECORD_UP_LOAD_SENSING_PRESSURE_SENSOR_ARRAY: // 128 bytes (64 words) for array storage
         return (EEPROM_PAGE_SIZE * 20l);

      case RECORD_UP_NOLOAD_ANGLE_SENSOR_ARRAY:       // 128 bytes (64 words) for array storage
         return (EEPROM_PAGE_SIZE * 22l);

      case RECORD_UP_NOLOAD_PRESSURE_SENSOR_ARRAY:    // 128 bytes (64 words) for array storage
         return (EEPROM_PAGE_SIZE * 24l);

      case RECORD_DN_LOAD_SENSING_ANGLE_SENSOR_ARRAY: // 128 bytes (64 words) for array storage
         return (EEPROM_PAGE_SIZE * 28l);

      case RECORD_DN_LOAD_SENSING_PRESSURE_SENSOR_ARRAY: // 128 bytes (64 words) for array storage
         return (EEPROM_PAGE_SIZE * 30l);

      case RECORD_DN_NOLOAD_ANGLE_SENSOR_ARRAY:       // 128 bytes (64 words) for array storage
         return (EEPROM_PAGE_SIZE * 32l);

      case RECORD_DN_NOLOAD_PRESSURE_SENSOR_ARRAY:    // 128 bytes (64 words) for array storage
         return (EEPROM_PAGE_SIZE * 34l);

	  case RECORD_MAGIC_LIFT_LOCKSET:    // 
         return (EEPROM_PAGE_SIZE * 36l);
 
	  case RECORD_MAGIC_ECULOCKLIB_DATA1:    // 
         return (EEPROM_PAGE_SIZE * 36l) +8;

	  case RECORD_MAGIC_HOURMETER:    // 
         return (EEPROM_PAGE_SIZE * 37l);
		 
	 case RECORD_MAGIC_OUTDOOR_MODE:    // 
         return (EEPROM_PAGE_SIZE * 38l);	 
		 
	 case RECORD_MAGIC_BATTERY_Level:
		return (EEPROM_PAGE_SIZE * 39l);
		
	 case RECORD_MAGIC_ECULOCKLIB_DATA2:    // 
		return (EEPROM_PAGE_SIZE * 40l);
		
	 case RECORD_MAGIC_ECULOCKLIB_DATA3:    // 
		return (EEPROM_PAGE_SIZE * 41l);
		
	 case RECORD_MAGIC_ECULOCKLIB_DATA4:    // 
		return (EEPROM_PAGE_SIZE * 42l);
	 case RECORD_MAGIC_ECULOCKLIB_DATA_BeforeRecovery:
	 	return (EEPROM_PAGE_SIZE * 43l);
	 case RECORD_MAGIC_ECULOCKLIB_DATA_RecoveryCnt:
	 	return (EEPROM_PAGE_SIZE * 44l);
   }
}

/*******************************************************************************
 *
 * PROCEDURE:  record_read()
 *
 * DESCRIPTION: It reads and verifies record stored in eeprom. If record
 *              is ok, it copies data to uses space.
 *
 * VARIABLE NAME  R/W DESCRIPTION
 * ---------------- --- --------------------------------------------------------
 ******************************************************************************/

EEPROM_ERR record_read(UINT8 bMagic, UINT8 bSize, UINT8  * pbRecordData)
{
   EEPROM_ERR eRet;
   UINT16 wAddress;
   UINT8 * pb;
   UINT8 ix;

   if(bSize > RECORD_DATA_LEN || (UINT8 *)0 == pbRecordData)
   {
      return EEPROM_RECORD_SIZE;

   }

   wAddress = MagicToAddress(bMagic);

   if((UINT16) - 1 == wAddress)
   {
      return EEPROM_NO_RECORD;
   }

   if(EEPROM_OK != (eRet = EE_Read(wAddress
                                   , (UINT8 *)&NVM_Cache
                                   , (bSize + RECORD_HEADER_LEN))))
   {
      return eRet;
   }

   if(bMagic != NVM_Cache.NVM_Header.bMagic
      || bSize != NVM_Cache.NVM_Header.bLength
      || (0 != (NVM_Cache.NVM_Header.bMagic
                ^ NVM_Cache.NVM_Header.bLength
                ^ NVM_Cache.NVM_Header.bSum
                ^ NVM_Cache.NVM_Header.bXor)))
   {
      return EEPROM_NO_RECORD;
   }

   pb = &NVM_Cache.abData[ 0];

   for(ix = 0; ix < bSize; ix++)
   {
      *pbRecordData++ = *pb;
      NVM_Cache.NVM_Header.bSum += *pb++;
   }

   if(NVM_Cache.NVM_Header.bSum)
   {
      return EEPROM_RECORD_DATA;
   }

   return EEPROM_OK;
}

/*******************************************************************************
 *
 * PROCEDURE:  record_write()
 *
 * DESCRIPTION: It writes record to eeprom.
 *              It doesn't verify eeprom data and record length must be less than page size.
 *              it is used to save unimportant data at one system cycle ( 10 millisecond ).
 *
 * VARIABLE NAME  R/W DESCRIPTION
 * ---------------- --- --------------------------------------------------------
 ******************************************************************************/

EEPROM_ERR record_write(UINT8 bMagic, UINT8 bSize, UINT8  * pbRecordData)
{
   UINT16 wAddress;
   UINT8 * pb;
   UINT8 ix;

   if(bSize > (EEPROM_PAGE_SIZE - RECORD_HEADER_LEN)
      || (UINT8 *)0 == pbRecordData)
   {
      return EEPROM_RECORD_SIZE;
   }

   wAddress = MagicToAddress(bMagic);

   if((UINT16) - 1 == wAddress)
   {
      return EEPROM_NO_RECORD;
   }

   pb = &NVM_Cache.abData[ 0];
   NVM_Cache.NVM_Header.bSum  = 0;
   for(ix = 0; ix < bSize; ix++)
   {
      *pb++ = *pbRecordData;
      NVM_Cache.NVM_Header.bSum += *pbRecordData++;
   }

   NVM_Cache.NVM_Header.bSum = -NVM_Cache.NVM_Header.bSum;

   NVM_Cache.NVM_Header.bMagic  = bMagic;
   NVM_Cache.NVM_Header.bLength = bSize;
   NVM_Cache.NVM_Header.bXor = NVM_Cache.NVM_Header.bMagic
                               ^ NVM_Cache.NVM_Header.bLength
                               ^ NVM_Cache.NVM_Header.bSum;
   return EE_Write(wAddress
                   , (UINT8 *)&NVM_Cache
                   , (bSize + RECORD_HEADER_LEN));
}

/*******************************************************************************
 *
 * PROCEDURE:  record_write_verify()
 *
 * DESCRIPTION: It writes record to eeprom and verify stored data.
 *              It take a number of system cycle to write and verify ( busy - wait)
 *              It used only at labview and speed-adjust when no real-time.
 *
 * VARIABLE NAME  R/W DESCRIPTION
 * ---------------- --- --------------------------------------------------------
 ******************************************************************************/

EEPROM_ERR record_write_verify(UINT8 bMagic, UINT8 bSize, UINT8  * pbRecordData)
{
   UINT16 wAddress;
   UINT8 * pb, * pbIn;
   UINT8 ix;
   UINT8 bTimeOut;
   EEPROM_ERR eRet;

   if(bSize > RECORD_DATA_LEN || (UINT8 *)0 == pbRecordData)
   {
      return EEPROM_RECORD_SIZE;
   }

   wAddress = MagicToAddress(bMagic);

   if((UINT16) - 1 == wAddress)
   {
      return EEPROM_NO_RECORD;
   }

   pbIn = pbRecordData;

   pb = &NVM_Cache.abData[ 0];
   NVM_Cache.NVM_Header.bSum  = 0;
   for(ix = 0; ix < bSize; ix++)
   {
      *pb++ = *pbRecordData;
      NVM_Cache.NVM_Header.bSum += *pbRecordData++;
   }

   NVM_Cache.NVM_Header.bSum = -NVM_Cache.NVM_Header.bSum;

   NVM_Cache.NVM_Header.bMagic  = bMagic;
   NVM_Cache.NVM_Header.bLength = bSize;
   NVM_Cache.NVM_Header.bXor = NVM_Cache.NVM_Header.bMagic
                               ^ NVM_Cache.NVM_Header.bLength
                               ^ NVM_Cache.NVM_Header.bSum;

   if(EEPROM_OK != (eRet = EE_WritePages(wAddress
                                         , (UINT8 *)&NVM_Cache
                                         , (bSize + RECORD_HEADER_LEN))))
   {
      return eRet;
   }

   //
   // It verifies written record
   //
   // it wait write operation to finish
   bTimeOut = 0;
   while(bTimeOut < 5)
   {
      if(TIMER_Data.bSystemCycles)
      {
         TIMER_Data.bSystemCycles = 0;
         bTimeOut++;
      }
   }

   if(EEPROM_OK != (eRet = EE_Read(wAddress
                                   , (UINT8 *)&NVM_Cache
                                   , (bSize + RECORD_HEADER_LEN))))
   {
      return eRet;
   }

   if(bMagic != NVM_Cache.NVM_Header.bMagic
      || bSize != NVM_Cache.NVM_Header.bLength
      || (0 != (NVM_Cache.NVM_Header.bMagic
                ^ NVM_Cache.NVM_Header.bLength
                ^ NVM_Cache.NVM_Header.bSum
                ^ NVM_Cache.NVM_Header.bXor)))
   {
      return EEPROM_NO_RECORD;
   }

   pb = &NVM_Cache.abData[ 0];

   for(ix = 0; ix < bSize; ix++)
   {
      if(*pbIn++ == *pb)
      {
         NVM_Cache.NVM_Header.bSum += *pb++;
      }
      else
      {
         return EEPROM_RECORD_DATA;
      }
   }

   if(NVM_Cache.NVM_Header.bSum)
   {
      return EEPROM_RECORD_DATA;
   }

   return EEPROM_OK;
}

/*******************************************************************************
 *
 * PROCEDURE:  EE_Writes()
 *
 * DESCRIPTION: It writes up to page size in eeprom.
 *
 * VARIABLE NAME  R/W DESCRIPTION
 * ---------------- --- --------------------------------------------------------
 ******************************************************************************/

EEPROM_ERR EE_Write(UINT16 wAddress, UINT8 * pbData, UINT16 wLength)
{
   UINT8 u8_delay = 0;

   // check length
   if(wLength > EEPROM_PAGE_SIZE)
   {
      return (EEPROM_NO_ADDRESS);
   }

   // start IIC
   while((0 != R_IICA0_Master_Start()) && (u8_delay < 50))
   {
      u8_delay++;
   }

   if(u8_delay >= 50)
   {
      return (EEPROM_BUSY);
   }

   // send write command
   if(0 != R_IICA0_Master_Write_Addr(EEPROM_ADDR))
   {
      return (EEPROM_BUSY);
   }

   // write address high
   if(0 != R_IICA0_Master_Write_Data((UINT8)((wAddress >> 8) & 0xFF)))
   {
      return (EEPROM_NO_ADDRESS);
   }

   // write address low
   if(0 != R_IICA0_Master_Write_Data((UINT8)(wAddress & 0xFF)))
   {
      return (EEPROM_NO_ADDRESS);
   }

   // write data
   while(wLength > 0)
   {
      R_IICA0_Master_Write_Data(*pbData);
      wLength--;
      pbData++;
   }

   // stop IIC
   R_IICA0_Master_Stop();

   return (EEPROM_OK);
}

/*******************************************************************************
 *
 * PROCEDURE:  EE_WritePages()
 *
 * DESCRIPTION: It writes data of length greater than a page to eeprom in
 *              busy-wait manner.
 *
 * VARIABLE NAME  R/W DESCRIPTION
 * ---------------- --- --------------------------------------------------------
 ******************************************************************************/


EEPROM_ERR EE_WritePages(UINT16 wAddress, UINT8 * pbData, UINT16 wLength)
{
   UINT16 u16_write_len;
   EEPROM_ERR en_write_result;
   UINT16 u16_delay;

   u16_write_len = (UINT16)EEPROM_PAGE_SIZE - (wAddress & (EEPROM_PAGE_SIZE - 1));

   while(wLength)
   {
      if(u16_write_len > wLength)
      {
         u16_write_len = wLength;
      }

      en_write_result = EE_Write(wAddress, pbData, u16_write_len);

      if(EEPROM_OK != en_write_result)
      {
         return en_write_result;
      }

      wLength -= u16_write_len;
      wAddress += u16_write_len;
      pbData += u16_write_len;
      u16_write_len = EEPROM_PAGE_SIZE;

      if(wLength)
      {
         // wait for 10ms, measured by CS+
         for(u16_delay = 0; u16_delay < 40000; u16_delay++);
      }
   }

   return (EEPROM_OK);
}

/*******************************************************************************
 *
 * PROCEDURE:  EE_Read()
 *
 * DESCRIPTION: It reads eeprom data.
 *
 * VARIABLE NAME  R/W DESCRIPTION
 * ---------------- --- --------------------------------------------------------
 ******************************************************************************/

EEPROM_ERR EE_Read(UINT16 wAddress, UINT8 * pbData, UINT16 wLength)
{
   UINT8 u8_delay = 0;

   // start IIC
   while((0 != R_IICA0_Master_Start()) && (u8_delay < 50))
   {
      u8_delay++;
   }

   if(u8_delay >= 50)
   {
      return (EEPROM_BUSY);
   }

   // send write command
   if(0 != R_IICA0_Master_Write_Addr(EEPROM_ADDR))
   {
      return (EEPROM_BUSY);
   }

   // write address high
   if(0 != R_IICA0_Master_Write_Data((UINT8)((wAddress >> 8) & 0xFF)))
   {
      return (EEPROM_NO_ADDRESS);
   }

   // write address low
   if(0 != R_IICA0_Master_Write_Data((UINT8)(wAddress & 0xFF)))
   {
      return (EEPROM_NO_ADDRESS);
   }

   // stop IIC
   if(0 != R_IICA0_Master_Stop())
   {
      return (EEPROM_BUSY);
   }

   // wait
   for(u8_delay = 100; u8_delay > 0; u8_delay--);

   // start IIC
   if(0 != R_IICA0_Master_Start())
   {
      return (EEPROM_BUSY);
   }

   // send read command
   if(0 != R_IICA0_Master_Read_Addr(EEPROM_ADDR))
   {
      return (EEPROM_BUSY);
   }

   // read data
   while(wLength > 1)
   {
      R_IICA0_Master_Read_Data(pbData, 0);
      wLength--;
      pbData++;
   }

   R_IICA0_Master_Read_Data(pbData, 1);
   // stop IIC
   R_IICA0_Master_Stop();

   return (EEPROM_OK);
}

/*******************************************************************************
 *
 * PROCEDURE:  EE_Writes_Byte()
 *
 * DESCRIPTION: It fill one byte to page size in eeprom.
 *
 * VARIABLE NAME  R/W DESCRIPTION
 * ---------------- --- --------------------------------------------------------
 ******************************************************************************/

EEPROM_ERR EE_Write_Byte(UINT16 wAddress, UINT8 bData, UINT16 wLength)
{
   UINT8 u8_delay = 0;

   // check length
   if(wLength > EEPROM_PAGE_SIZE)
   {
      return (EEPROM_NO_ADDRESS);
   }

   // start IIC
   while((0 != R_IICA0_Master_Start()) && (u8_delay < 50))
   {
      u8_delay++;
   }

   if(u8_delay >= 50)
   {
      return (EEPROM_BUSY);
   }

   // send write command
   if(0 != R_IICA0_Master_Write_Addr(EEPROM_ADDR))
   {
      return (EEPROM_BUSY);
   }

   // write address high
   if(0 != R_IICA0_Master_Write_Data((UINT8)((wAddress >> 8) & 0xFF)))
   {
      return (EEPROM_NO_ADDRESS);
   }

   // write address low
   if(0 != R_IICA0_Master_Write_Data((UINT8)(wAddress & 0xFF)))
   {
      return (EEPROM_NO_ADDRESS);
   }

   // write data
   while(wLength > 0)
   {
      R_IICA0_Master_Write_Data(bData);
      wLength--;
   }

   // stop IIC
   R_IICA0_Master_Stop();

   return (EEPROM_OK);
}

#else

#error " no implementation of eeprom routines"

#endif // COMPILE_EEPROM








